﻿using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.SharePoint;
using RoketSolutions.SP2010.Infrostructure.Data.Entities;
using RoketSolutions.SP2010.Infrostructure.Data.Repositories.Helpers;
using RoketSolutions.SP2010.Infrostructure.Data.Repositories.Interfaces;

namespace RoketSolutions.SP2010.Infrostructure.Data.Repositories
{
    public abstract class SPRepository<TEntity> : IListRepository<TEntity>, IDisposable
        where TEntity : ListEntityBase
    {
        protected ListResolver ListResolver { get; set; }

        protected abstract string ListName { get; }

        public void Init(string siteUrl)
        {
            ListResolver = new ListResolver(siteUrl, ListName);
        }

        public void Init(SPWeb web)
        {
            ListResolver = new ListResolver(web, ListName);
        }

        public TEntity CreateNew()
        {
            var item = ListResolver.List.AddItem();
            return Wrap(item);
        }

        protected abstract TEntity Wrap(SPListItem item);

        protected abstract SPListItem Unwrap(TEntity entity);

        protected virtual IList<TEntity> GetItemsWithQuery(SPQuery spQuery)
        {
            var items = ListResolver.List.GetItems(spQuery);

            return ConvertToEntityList(items);
        }

        protected virtual IList<TEntity> GetItemsWithQuery(String queryText)
        {
            return GetItemsWithQuery(new SPQuery { Query = queryText });
        }

        protected virtual IList<TEntity> GetItemsWithQuery(String queryText, String viewFields)
        {
            return GetItemsWithQuery(new SPQuery { Query = queryText, ViewFields = viewFields });
        }

        protected void CreateLogicalQuery(IList<string> elements, string logicOperator, ref string finalQuery)
        {
            if (elements.Count > 1)
            {
                var elementIndex = 0;
                var pairsCount = Math.Floor((double)elements.Count / 2);
                if (pairsCount > 0)
                {
                    var newElements = new List<string>();
                    for (int i = 0; i < pairsCount; i++)
                    {
                        newElements.Add(string.Format("<{0}>{1}{2}</{0}>", logicOperator, elements[elementIndex], elements[elementIndex + 1]));
                        elementIndex += 2;
                    }
                    if (elements.Count % 2 == 1)
                    {
                        newElements.Add(elements[elements.Count - 1]);
                    }

                    CreateLogicalQuery(newElements, logicOperator, ref finalQuery);
                }
            }
            else
                if (elements.Count == 1)
                    finalQuery = elements[0];
        }

        protected IList<TEntity> ConvertToEntityList(SPListItemCollection itemCollection)
        {
            var result = new List<TEntity>();

            if (itemCollection != null && itemCollection.Count > 0)
            {
                result.AddRange(from SPListItem item in itemCollection select Wrap(item));
            }

            return result;
        }


        public TEntity FindById(int itemId)
        {
            SPListItem item = null;

            try
            {
                item = ListResolver.List.GetItemById(itemId);
            }
            catch (Exception)
            {
            }

            return item != null ? Wrap(item) : null;
        }

        public IList<TEntity> Find(Func<TEntity, bool> filter)
        {
            var result = new List<TEntity>();
            foreach (SPListItem item in ListResolver.List.Items)
            {
                var entity = Wrap(item);
                if (filter == null)
                    result.Add(entity);
                else if (filter(entity))
                    result.Add(entity);
            }
            return result;
        }

        public TEntity FindSingle(Func<TEntity, bool> filter)
        {
            var result = Find(filter);
            if (result.Count > 1)
            {
                throw new InvalidOperationException("Query matches more than one entry");
            }
            return result.Any() ? result[0] : null;
        }

        public IList<TEntity> FindAll()
        {
            return Find(null);
        }

        public void Dispose()
        {
            if (ListResolver != null)
                ListResolver.Dispose();
        }

        
    }

}